package main

import (
	"fmt"
	"log"
	"os/exec"
	"strings"
	"unicode"
	"unicode/utf8"
)

// （9分）
// 给定如下的正则表达式 (a|b)((c|d)*)，请完成如下练习：
// （1）使用Thompson算法，将该正则表达式转换成非确定状态有限自动机（NFA）；
// （2）使用子集构造算法，将该上述的非确定有限状态自动机（NFA）转换成确定状态有限自动机（DFA）；
// （3）使用Hopcroft算法，对该DFA最小化。

type item struct {
	typ     itemType // The type of this item.
	pos     int      // The starting position, in bytes, of this item in the input string.
	linepos int
	line    int
	val     string // The value of this item.
}

func init() {
	log.SetFlags(log.Lshortfile)
}

const eof = -1

func (i item) String() string {
	return fmt.Sprintf("%-13s%d:%d\t%q", itemStringMap[i.typ], i.line, i.linepos, i.val)
}

type itemType int

const (
	itemError itemType = iota
	itemEOF
	itemUnion         // ab
	itemConcatenation // a|b
	itemKleeneStar    // a*
	itemLeftParen     // (
	itemRightParen    // )
)

var itemStringMap = map[itemType]string{
	itemError:         "itemError",
	itemEOF:           "itemEOF",
	itemUnion:         "itemUnion",
	itemConcatenation: "itemConcatenation",
	itemKleeneStar:    "itemKleeneStar",
	itemLeftParen:     "itemLeftParen",
	itemRightParen:    "itemRightParen",
}

func (e itemType) String() string {
	v, ok := itemStringMap[e]
	if !ok {
		panic("")
	}
	return v
}

type stateFn func(*lexer) stateFn
type lexer struct {
	name       string    // the name of the input; used only for error reports
	input      string    // the string being scanned
	state      stateFn   // the next lexing function to enter
	pos        int       // current position in the input
	start      int       // start position of this item
	width      int       // width of last rune read from input
	lastPos    int       // position of most recent item returned by nextItem
	items      chan item // channel of scanned items
	parenDepth int       // nesting depth of ( ) exprs
}

// next returns the next rune in the input.
func (l *lexer) next() rune {
	if int(l.pos) >= len(l.input) {
		l.width = 0
		return eof
	}
	r, w := utf8.DecodeRuneInString(l.input[l.pos:])
	l.width = w
	l.pos += l.width
	return r
}

// peek returns but does not consume the next rune in the input.
func (l *lexer) peek() rune {
	r := l.next()
	l.backup()
	return r
}

// backup steps back one rune. Can only be called once per call of next.
func (l *lexer) backup() {
	l.pos -= l.width
}

// emit passes an item back to the client.
func (l *lexer) emit(t itemType) {
	line, linepos := l.lineNumber()
	l.items <- item{
		typ:     t,
		pos:     l.start,
		val:     l.input[l.start:l.pos],
		linepos: linepos,
		line:    line,
	}
	l.start = l.pos
}
func (l *lexer) emitValue(t itemType, value string) {
	line, linepos := l.lineNumber()
	l.items <- item{
		typ:     t,
		pos:     l.start,
		val:     value,
		linepos: linepos,
		line:    line,
	}
	l.start = l.pos
}

// ignore skips over the pending input before this point.
func (l *lexer) ignore() {
	l.start = l.pos
}

// accept consumes the next rune if it's from the valid set.
func (l *lexer) accept(valid string) bool {
	if strings.IndexRune(valid, l.next()) >= 0 {
		return true
	}
	l.backup()
	return false
}

// acceptRun consumes a run of runes from the valid set.
func (l *lexer) acceptRun(valid string) {
	for strings.IndexRune(valid, l.next()) >= 0 {
	}
	l.backup()
}

// lineNumber reports which line we're on, based on the position of
// the previous item returned by nextItem. Doing it this way
// means we don't have to worry about peek double counting.
func (l *lexer) lineNumber() (line, linepos int) {
	line = 1 + strings.Count(l.input[:l.start], "\n")
	p := strings.LastIndex(l.input[:l.start], "\n")
	if p == -1 {
		linepos = l.start + 1
	} else {
		linepos = l.start - p
	}
	return
}

// errorf returns an error token and terminates the scan by passing
// back a nil pointer that will be the next state, terminating l.nextItem.
func (l *lexer) errorf(format string, args ...interface{}) stateFn {

	line, linepos := l.lineNumber()
	l.items <- item{
		typ:     itemError,
		pos:     l.start,
		val:     fmt.Sprintf(format, args...),
		linepos: linepos,
		line:    line,
	}
	return nil
}

// nextItem returns the next item from the input.
func (l *lexer) nextItem() item {
	item := <-l.items
	l.lastPos = item.pos
	return item
}

// lex creates a new scanner for the input string.
func lex(name, input string) *lexer {

	l := &lexer{
		name:  name,
		input: input,
		items: make(chan item),
	}
	go l.run()
	return l
}

// run runs the state machine for the lexer.
func (l *lexer) run() {
	for l.state = lexText; l.state != nil; {
		l.state = l.state(l)
	}
}

// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore.
func isAlphaNumeric(r rune) bool {
	return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
}

func isAlpha(r rune) bool {
	return r == '_' || unicode.IsLetter(r)
}

// lexText scans until an opening action delimiter, "{{".
func lexText(l *lexer) stateFn {
	first := l.next()
	switch {
	case first == eof:
		l.emit(itemEOF)
		return nil
	case first == '(':
		l.emit(itemLeftParen)
	case first == ')':
		l.emit(itemRightParen)
	case first == '|':
		l.emit(itemConcatenation)
	case first == '*':
		l.emit(itemKleeneStar)
	default:
		l.backup()
		return lexUnion(l)
		// l.errorf("unknow lex %c", first)
	}
	return lexText
}

func lexUnion(l *lexer) stateFn {
	escape := false
	var ans []rune
Loop:
	for {
		r := l.next()
		if escape {
			switch r {
			case eof:
				l.backup()
				break Loop
			case 'n':
				ans = append(ans, '\n')
			case 't':
				ans = append(ans, '\t')
			default:
				ans = append(ans, r)
			}
			escape = false
		} else {
			switch r {
			case eof:
				l.backup()
				break Loop
			case '\\':
				escape = true
			case '|', '*', '(', ')':
				l.backup()
				l.emitValue(itemUnion, string(ans))
				return lexText
			default:
				ans = append(ans, r)
			}
		}
	}
	l.emitValue(itemUnion, string(ans))
	return lexText
}

func main() {
	// l := lex("testlex", `((ab(c|d|e)d|e)*)z`)
	l := lex("testlex", `a(b|c)*`)
	c := 0
	var itemList []item
	for {
		c++
		if c > 30 {
			log.Println("to much")
			return
		}
		item := l.nextItem()
		itemList = append(itemList, item)
		// log.Println("item", item.String())
		if item.typ == itemEOF || item.typ == itemError {
			break
		}
	}
	log.Println("item", itemList)
	log.Println(parse(itemList))
	v := NewNode(
		1, itemList[0],
		NewNode(2, itemList[1], nil),
		NewNode(3, itemList[5], NewNode(1, itemList[4], nil)),
	)
	dot := draw(v)
	log.Println(dot)
	cmd := exec.Command("bash", "-c", `dot -Tsvg -o b.svg`)
	cmd.Stdin = strings.NewReader(dot)
	ans, err := cmd.CombinedOutput()
	log.Println(string(ans), err)
}

type itemer struct {
	itemList []item
	now      int
}

func (p *itemer) next() *item {
	if p.now >= len(p.itemList) {
		return nil
	}
	p.now++
	return &p.itemList[p.now-1]
}

func (p *itemer) backup() {
	p.now--
	if p.now < 0 {
		panic("")
	}
}

func (p *itemer) peek() (r *item) {
	r = p.next()
	p.backup()
	return
}

func parse(itemList []item) *Node {
	lex := &itemer{itemList, 0}
	for {
		r := lex.next()
		if r == nil {
			break
		}
		log.Println(r)
	}
	return nil
}

func draw(node *Node) string {
	dot := `
digraph finite_state_machine {
  rankdir=LR;
  //size="8,5"
  node [shape = circle];

`
	var do func(node *Node)
	do = func(node *Node) {
		if node == nil {
			return
		}
		for i := 0; i < len(node.To); i++ {
			if node.To[i] != nil {
				dot += fmt.Sprintf("S%d -> S%d [label=\"%v\"]\n", node.Id, node.To[i].Id, node.To[i].item.val)
			}
			do(node.To[i])
		}
	}
	do(node)
	dot += "}"
	return dot
}

type Node struct {
	Id int
	// Empty bool
	// Value rune
	item item
	To   []*Node
}

func NewNode(i int, item item, to ...*Node) *Node {
	return &Node{i, item, to}
}
